﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Diagnostics;
using System.Timers;

namespace Cherry.Agile.ServiceModel.Monitor {
    /// <summary>
    /// 服务状态侦听服务
    /// </summary>
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public sealed class MonitorService : IMonitorService {
        internal static event EventHandler<MonitorNotifyEventArgs> BackendServerActive;
        internal static event EventHandler<MonitorNotifyEventArgs> BackendServerTearDown;

        static Dictionary<string, DateTime> __backendServerState = new Dictionary<string, DateTime>();
        static object __sync = new object();
        static ServiceHost __host;
        static Timer __timer = new Timer();
        static int __timeout;
        static MonitorService() {
            if (!Config.RempServiceModelSection.Current.Monitor.Enable)
                return;
            __timeout = Config.RempServiceModelSection.Current.Monitor.Timeout;
            __timeout = Math.Max(5, __timeout);
            __timeout = Math.Min(30, __timeout);
            __timer.Interval = __timeout * 1000 + 1000;

            __timer.Elapsed += new ElapsedEventHandler(__timer_Elapsed);

            __host = new ServiceHost(typeof(MonitorService));
        }



        static void OnBackendServerTearDown(MonitorService sender, string[] addresses) {
            if (BackendServerTearDown != null)
                BackendServerTearDown(sender, new MonitorNotifyEventArgs(addresses, string.Empty));
        }

        static void OnBackendServerActive(MonitorService sender, string address, string tag) {

            if (!__backendServerState.ContainsKey(address)) {
                lock (__sync)
                    __backendServerState.Add(address, DateTime.Now);
            } else {
                var date = __backendServerState[address];
                if (date.AddSeconds(__timeout) <= DateTime.Now)//online
                    if (BackendServerActive != null)
                        BackendServerActive(sender, new MonitorNotifyEventArgs(new string[] { address }, tag));

                __backendServerState[address] = DateTime.Now;
            }
        }

        static void __timer_Elapsed(object sender, ElapsedEventArgs e) {
            string[] addresses;
            lock (__sync) {
                addresses = __backendServerState
                            .Where(kv => kv.Value.AddSeconds(__timeout) <= DateTime.Now)
                            .Select(kv => kv.Key).ToArray();
            }
            if (addresses.Length > 0 && BackendServerTearDown != null)
                BackendServerTearDown(sender, new MonitorNotifyEventArgs(addresses, string.Empty));
        }

        public void Notify(string address, string name, string tag) {
            address = address.TrimEnd('/') + "/";
            OnBackendServerActive(this, address, tag);
        }

        public static void Start() {
            if (__host == null)
                return;
            if (__host.State != CommunicationState.Opened) {
                __host.Open();
                __timer.Start();
            }
            Trace.TraceInformation("Monitor\tservice started.");
        }

        public static void Stop() {
            if (__host == null)
                return;

            if (__host.State != CommunicationState.Closed) {
                __timer.Stop();
                __host.Close();
            }
            Trace.TraceInformation("Monitor\tservice stoped.");
        }
    }

    class MonitorNotifyEventArgs : EventArgs {
        public string[] Addresses { get; private set; }
        public string Tag { get; private set; }

        public MonitorNotifyEventArgs(string[] addresses, string tag) {
            Addresses = addresses;
            Tag = tag;
        }
    }
}
